Improve the error message for ambiguous specs
authorAlex Crichton <alex@alexcrichton.com>
Sat, 27 Sep 2014 04:45:51 +0000 (21:45 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 29 Sep 2014 23:55:45 +0000 (16:55 -0700)
src/cargo/core/resolver.rs
tests/test_cargo_compile_git_deps.rs

index f47aa70557c13068723c5e687f289a60f54848cb..d33bfe618b60fb0ec851cbdfd1ca4759ce245599 100644 (file)
@@ -1,5 +1,4 @@
-use std::collections::{HashMap, HashSet};
-use std::collections::hashmap::{Occupied, Vacant};
+use std::collections::hashmap::{HashMap, HashSet, Occupied, Vacant};
 use std::fmt;
 use semver;
 
@@ -222,17 +221,44 @@ impl Resolve {
             None => return Err(human(format!("package id specification `{}` \
                                               matched no packages", spec))),
         };
-        match ids.next() {
+        return match ids.next() {
             Some(other) => {
-                let mut msg = format!("Ambiguous package id specification: \
-                                       `{}`\nMatching packages:\n  {}\n  {}",
-                                      spec, ret, other);
-                for id in ids {
-                    msg = format!("{}\n  {}", msg, id);
-                }
+                let mut msg = format!("There are multiple `{}` packages in \
+                                       your project, and the specification \
+                                       `{}` is ambiguous.\n\
+                                       Please re-run this command \
+                                       with `-p <spec>` where `<spec>` is one \
+                                       of the following:",
+                                      spec.get_name(), spec);
+                let mut vec = vec![ret, other];
+                vec.extend(ids);
+                minimize(&mut msg, vec, &spec);
                 Err(human(msg))
             }
             None => Ok(ret)
+        };
+
+        fn minimize(msg: &mut String,
+                    ids: Vec<&PackageId>,
+                    spec: &PackageIdSpec) {
+            let mut version_cnt = HashMap::new();
+            for id in ids.iter() {
+                let slot = match version_cnt.entry(id.get_version()) {
+                    Occupied(e) => e.into_mut(),
+                    Vacant(e) => e.set(0u),
+                };
+                *slot += 1;
+            }
+            for id in ids.iter() {
+                if version_cnt[id.get_version()] == 1 {
+                    msg.push_str(format!("\n  {}:{}", spec.get_name(),
+                                 id.get_version()).as_slice());
+                } else {
+                    msg.push_str(format!("\n  {}",
+                                         PackageIdSpec::from_package_id(*id))
+                                        .as_slice());
+                }
+            }
         }
     }
 
index 1ab1f0e85020ae957fff33c4b9e0253504acdec1..ad805d85eb645667b717289244943cf57479e077 100644 (file)
@@ -1328,3 +1328,62 @@ test!(warnings_in_git_dep {
                              COMPILING, p.url()))
         .with_stderr(""));
 })
+
+test!(update_ambiguous {
+    let foo1 = git_repo("foo1", |project| {
+        project.file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.5.0"
+            authors = ["wycats@example.com"]
+        "#)
+        .file("src/lib.rs", "")
+    }).assert();
+    let foo2 = git_repo("foo2", |project| {
+        project.file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.6.0"
+            authors = ["wycats@example.com"]
+        "#)
+        .file("src/lib.rs", "")
+    }).assert();
+    let bar = git_repo("bar", |project| {
+        project.file("Cargo.toml", format!(r#"
+            [package]
+            name = "bar"
+            version = "0.5.0"
+            authors = ["wycats@example.com"]
+
+            [dependencies.foo]
+            git = '{}'
+        "#, foo2.url()).as_slice())
+        .file("src/lib.rs", "")
+    }).assert();
+
+    let p = project("project")
+        .file("Cargo.toml", format!(r#"
+            [project]
+            name = "project"
+            version = "0.5.0"
+            authors = []
+            [dependencies.foo]
+            git = '{}'
+            [dependencies.bar]
+            git = '{}'
+        "#, foo1.url(), bar.url()).as_slice())
+        .file("src/main.rs", "fn main() {}");
+
+    assert_that(p.cargo_process("generate-lockfile"), execs().with_status(0));
+    assert_that(p.process(cargo_dir().join("cargo")).arg("update")
+                 .arg("-p").arg("foo"),
+                execs().with_status(101)
+                       .with_stderr("\
+There are multiple `foo` packages in your project, and the specification `foo` \
+is ambiguous.
+Please re-run this command with `-p <spec>` where `<spec>` is one of the \
+following:
+  foo:0.[..].0
+  foo:0.[..].0
+"));
+})